#pragma once
#include<iostream>
#include<string>
#include<memory>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#include"protocol.hpp"

using namespace std;

class tcpClient
{
public:
    tcpClient(const string& serverip, const uint16_t& port)
    :_sockfd(-1)
    ,_serverip(serverip)
    ,_serverport(port)
    {}

    void InitClient()
    {
        // 创建socket(套接字)
        _sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if(_sockfd < 0)
        {
            // 客户端也是可以弄日志的[类似之前用某些软件崩溃后，会弹出一个是否同意上传日志，]
            // 这里我没弄, 只弄了服务器的日志
            cerr << "socket create error" << endl;
            exit(2);
        }
        cout << "create socket success\n";
        
        //2.bind ip和port【tcp的客户端bind同样不需要显示bind， 由os来管】
        // 并且客户端不需要listen， 所以客户端不需要accept
        // 对于客户端，要向服务器端发起链接，让服务端listen到，然后accept

    }

    void run()
    {
        // 填充结构体，为后来给服务端传输数据做准备
        struct sockaddr_in server;
        bzero(&server, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_port = htons(_serverport);
        server.sin_addr.s_addr = inet_addr(_serverip.c_str());

        // 准备与服务端通信，但tcp通信之前需要先建立链接，对于客户端来说要向服务端发起新链接
        if(connect(_sockfd, (struct sockaddr*)&server, sizeof(server)) != 0)
        {
            //建立链接失败肯定没发继续通信了, 但是也没必要exit
            cerr << "socket connect error" << endl;
        }
        else
        {
            // 成功建立连接之后就可以通信了
            string message;
            string inbuffer;
            while(true)
            {
                cout << "Enter: \n";
                getline(cin, message); // 用户输入的1+1或者 1 + 1
                // 获取到用户的输入信息之后，客户端也得遵守协议，向服务器端发送请求，然后接受服务器的响应
                Request req = ParseLine(message); // 要先变成结构化的数据
                // cout << req._x << req._op << req._y << endl;
                string content;
                req.serialize(&content); //序列化
                // cout << "content: " << content << endl;
                string send_str;
                if(!enLength(send_str, content)) //形成报文
                    continue; //很难失败的说
                // cout << "send_str: "<<  send_str << endl;
                send(_sockfd, send_str.c_str(), send_str.size(), 0); // 这里发送有问题，先不管                

                // 接受服务器发回来的响应
                // 首先要保证能读到一份完整的报文
                string res_str; //存放读取到的完整的报文'
                if(!recvPackage(_sockfd, inbuffer, &res_str))
                    continue;
                // cout << res_str << endl;
                string res_content; //用于接受有效载荷， 即"exitcode result"

                if(!deLength(res_str, &res_content)) //  从报文分离有效载荷
                    continue;
                // cout << res_content << endl;
                Response res;
                res.deserialize(res_content); //反序列化
                // cout << res._exitcode << res._result << endl;
                if(res._exitcode == 0)
                {
                    // 只有当exitcode为0时，才是没有出现错误，才有答案
                    cout << "exitcode: " << res._exitcode << " result: " << res._result << endl;
                }
                else //走到这里就说明出现了计算错误，比如除0错误
                {
                    cout << "exitcode: " << res._exitcode << " result: " << res._result << endl;
                    continue;
                }
                
                
            }
    
        }

    }

    // 将用户输入的数据进行字符串提取
    Request ParseLine(const string& line)
    {
        // 首先line一定是 1+1,或者123+312之类的字符串
        int status = 0; // 0表示遇到操作符之前，1表示遇到了，2表示操作符之后
        int i = 0; 
        int size = line.size();
        char op;
        string left, right; //left放op左边的数字，right放op右边的数字
        while(i < size)
        {
            switch(status)
            {
                case 0:
                    if(!isdigit(line[i])) //判断是否为数字
                    {
                        // 走进来就说明不是数字，是op
                        op = line[i];
                        status = 1;
                    }
                    else
                    {
                        left.push_back(line[i++]);
                    }
                break;
                case 1:
                        i++;
                        status = 2;
                break;
                case 2:
                        right.push_back(line[i++]);
                break;
            }
        }
        // 此时就将切割出来的数据进行结构化处理
        // Request req;
        // req._x = stoi(left);
        // req._y = stoi(right);
        // req._op = op;
        // return req;
        return Request(stoi(left), op, stoi(right)); // 等价上面的几行代码

    }

    ~tcpClient() 
    {
        if(_sockfd >= 0)
            close(_sockfd); //这里手动关一下，其实服务器进程一关，这个文件描述符也就关了
    }
private:
    int _sockfd;
    string _serverip;
    uint16_t _serverport;
}; 
